package junitj2me.framework;

import java.util.*;

/**
 * A <code>TestResult</code> collects the results of executing
 * a test case. It is an instance of the Collecting Parameter pattern.
 * The test framework distinguishes between <i>failures</i> and <i>errors</i>.
 * A failure is anticipated and checked for with assertions. Errors are
 * unanticipated problems like an <code>ArrayIndexOutOfBoundsException</code>.
 *
 * @see Test
 */
public class TestResult extends Object
{
    protected Vector fFailures;
    protected Vector fErrors;
    protected int    fRunTests;
    private boolean  fStop;
    private long     iStartTime;
    private long     iTotalElapsedTime;

    public TestResult()
    {
        fFailures = new Vector();
        fErrors = new Vector();
        fRunTests = 0;
        fStop = false;
    }

    /**
     * Adds an error to the list of errors. The passed in exception
     * caused the error.
     */
    public synchronized void addError( Test test, Throwable t )
    {
        fErrors.addElement( new TestFailure( test, t ) );
    }

    /**
     * Adds a failure to the list of failures. The passed in exception
     * caused the failure.
     */
    public synchronized void addFailure( Test test, AssertionFailedError t )
    {
        fFailures.addElement( new TestFailure( test, t ) );
    }

    /**
     * Informs the result that a test was completed.
     */
    public void endTest( Test test )
    {
        long elapsedTimeForThisTest = System.currentTimeMillis() - iStartTime;

        iTotalElapsedTime += elapsedTimeForThisTest;
    }

    /**
     * Gets the number of detected errors.
     */
    public synchronized int errorCount()
    {
        return fErrors.size();
    }

    /**
     * Returns an Enumeration for the errors
     */
    public synchronized Enumeration errors()
    {
        return fErrors.elements();
    }

    /**
     * Gets the number of detected failures.
     */
    public synchronized int failureCount()
    {
        return fFailures.size();
    }

    /**
     * Returns an Enumeration for the failures
     */
    public synchronized Enumeration failures()
    {
        return fFailures.elements();
    }

    /**
     * Runs a TestCase.
     */
    protected void run( final TestCase test )
    {
        startTest( test );
        Protectable p = new Protectable()
        {
            public void protect() throws Throwable
            {
                test.runBare();
            }
        };
        runProtected( test, p );

        endTest( test );
    }

    /**
     * Gets the number of run tests.
     */
    public synchronized int runCount()
    {
        return fRunTests;
    }

    /**
     * Runs a TestCase.
     */
    public void runProtected( final Test test, Protectable p )
    {
        try
        {
            p.protect();
        }
        catch( AssertionFailedError e )
        {
            addFailure( test, e );
        }
        //catch( ThreadDeath e )
        //{ // don't catch ThreadDeath by accident
        //    throw e;
        //}
        catch( Throwable e )
        {
            addError( test, e );
        }
    }

    /**
     * Checks whether the test run should stop
     */
    public synchronized boolean shouldStop()
    {
        return fStop;
    }

    /**
     * Informs the result that a test will be started.
     */
    public void startTest( Test test )
    {
        iStartTime = System.currentTimeMillis();

        final int count = test.countTestCases();
        synchronized( this )
        {
            fRunTests += count;
        }
    }

    /**
     * Marks that the test run should stop.
     */
    public synchronized void stop()
    {

        fStop = true;
    }

    /**
     * Returns whether the entire test was successful or not.
     */
    public synchronized boolean wasSuccessful()
    {
        return failureCount() == 0 && errorCount() == 0;
    }

    public long totalElapsedTime()
    {
        return iTotalElapsedTime;
    }

}